use serde_json;
use std::sync::Arc;
use std::ops::Deref;

use core::tick::Tick;
use std::collections::HashMap;


#[derive(Debug)]
pub struct NodeData<T>(Arc<T>);

impl<T> NodeData<T> {
    /// Create new `Data` instance.
    ///
    /// Internally `Data` type uses `Arc`. if your data implements
    /// `Send` + `Sync` traits you can use `web::Data::new()` and
    /// avoid double `Arc`.
    pub fn new(state: T) -> NodeData<T> {
        NodeData(Arc::new(state))
    }
    /// Get reference to inner app data.
    pub fn get_ref(&self) -> &T {
        self.0.as_ref()
    }
    /// Convert to the internal Arc<T>
    pub fn into_inner(self) -> Arc<T> {
        self.0
    }
}

impl<T> Deref for NodeData<T> {
    type Target = Arc<T>;

    fn deref(&self) -> &Arc<T> {
        &self.0
    }
}

impl<T> Clone for NodeData<T> {
    fn clone(&self) -> NodeData<T> {
        NodeData(self.0.clone())
    }
}

#[derive(Debug, PartialEq)]
pub enum NodeStatus {
    None,
    SUCCESS,
    FAILURE,
    RUNNING,
    ERROR
}

#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct BaseNode {
    pub id: String,
    pub category: String,
    pub name: String,
    pub title: String,
    pub nodes: Option<HashMap<String, BaseNode>>,
    pub children: Option<Vec<String>>,
    pub child: Option<String>,
    pub description: String,
    pub properties: HashMap<String, serde_json::Value>,
    // pub debug: bool
}

lazy_static! {
    pub static ref BLACKBOARD_SCOPE_IS_OPEN: String = "_scope_is_open_".to_string();
    pub static ref BLACKBOARD_WAIT_START_TIME: String = "_wait_start_time_".to_string();
    pub static ref BLACKBOARD_MEM_RUNNING_CHILD: String = "_mem_running_child".to_string();
    pub static ref BLACKBOARD_LIMITER_LOOP: String = "_limiter_loop_".to_string();
    pub static ref BLACKBOARD_REPEATER_LOOP: String = "_repeater_loop_".to_string();
    pub static ref BLACKBOARD_MAX_TIME: String = "_max_time_".to_string();
}
impl BaseNode {
    pub fn new(config: serde_json::Value) -> Result<BaseNode, String> {
        match serde_json::from_value(config) {
            Ok(_node) => {
                Ok(_node)
            }
            Err(_e) => {
                Err(_e.to_string())
            }
        }
    }
    pub fn _open(node: NodeData<BaseNode>, tick: &mut Tick, t_base_node: &dyn TBaseNode) {
        tick.open_node(node.clone());
        tick.blackboard.set(&*BLACKBOARD_SCOPE_IS_OPEN, json!(true), &tick.tree.id, &node.id);
        t_base_node.open(tick);
    }

    pub fn _enter(node: NodeData<BaseNode>, tick: &mut Tick, t_base_node: &dyn TBaseNode) {
        tick.enter_node(node.clone());
        t_base_node.enter(tick);
    }

    pub fn _tick(node: NodeData<BaseNode>, tick: &mut Tick, t_base_node: &dyn TBaseNode) -> NodeStatus {
        tick.tick_node(node.clone());
        t_base_node.tick(tick)
    }

    pub fn _close(node: NodeData<BaseNode>, tick: &mut Tick, t_base_node: &dyn TBaseNode) -> NodeStatus {
        tick.close_node(node.clone());
        tick.blackboard.delete(&*BLACKBOARD_SCOPE_IS_OPEN, &tick.tree.id, &node.id);
        t_base_node.close(tick)
    }

    pub fn _exit(node: NodeData<BaseNode>, tick: &mut Tick, t_base_node: &dyn TBaseNode) -> NodeStatus {
        tick.exit_node(node.clone());
        t_base_node.exit(tick)
    }

    pub fn _execute(node: NodeData<BaseNode>, tick: &mut Tick, t_base_node: &dyn TBaseNode) -> NodeStatus {
        #[cfg(test)]
        println!("_execute {:?}", node.title);
        // ENTER
        BaseNode::_enter(node.clone(), tick, t_base_node);
        match tick.blackboard.get(&*BLACKBOARD_SCOPE_IS_OPEN, &tick.tree.id, &node.id) {
            Some(_) => {}
            None => {
                // OPEN
                BaseNode::_open(node.clone(), tick, t_base_node);
            }
        };

        // TICK
        let status = BaseNode::_tick(node.clone(), tick, t_base_node);

        // CLOSE
        match status {
            NodeStatus::RUNNING => {
            }
            _ => {
                BaseNode::_close(node.clone(), tick, t_base_node);
            }
        };

        // EXIT
        BaseNode::_exit(node.clone(), tick, t_base_node);
        status
    }
}

pub trait TBaseNode {
    fn get_base_node_ref(&self) -> NodeData<BaseNode>;
    fn add_child(&mut self, child: Box<TBaseNode>) {}
    fn execute(&self, _tick: &mut Tick) -> NodeStatus {
        NodeStatus::None
    }
    fn tick(&self, _tick: &mut Tick) -> NodeStatus { NodeStatus::None }
    fn enter(&self, _tick: &mut Tick) -> NodeStatus { NodeStatus::None }
    fn exit(&self, _tick: &mut Tick) -> NodeStatus { NodeStatus::None }
    fn close(&self, _tick: &mut Tick) -> NodeStatus { NodeStatus::None }
    fn open(&self, _tick: &mut Tick) {}
}
//
// pub trait TBaseNodeConstructor {
//     type NodeType: Sized;
//     fn new(_config: serde_json::Value)->Result<Self::NodeType, String> {Err("not implement".to_string())}
// }
